export default class {
  constructor (options) {
    this.bufferSize = options.bufferSize || 4096
    this.sampleRate = options.sampleRate
    this.samples    = options.samples
  }

  finish () {
    this._joinSamples()

    let buffer = new ArrayBuffer(44 + this.samples.length * 2)
    let view   = new DataView(buffer)

    this._writeString(view, 0, 'RIFF')                       // RIFF identifier
    view.setUint32(4, 36 + this.samples.length * 2, true)    // RIFF chunk length
    this._writeString(view, 8, 'WAVE')                       // RIFF type
    this._writeString(view, 12, 'fmt ')                      // format chunk identifier
    view.setUint32(16, 16, true)                             // format chunk length
    view.setUint16(20, 1, true)                              // sample format (raw)
    view.setUint16(22, 1, true)                              // channel count
    view.setUint32(24, this.sampleRate, true)                // sample rate
    view.setUint32(28, this.sampleRate * 4, true)            // byte rate (sample rate * block align)
    view.setUint16(32, 4, true)                              // block align (channel count * bytes per sample)
    view.setUint16(34, 16, true)                             // bits per sample
    this._writeString(view, 36, 'data')                      // data chunk identifier
    view.setUint32(40, this.samples.length * 2, true)        // data chunk length

    this._floatTo16BitPCM(view, 44, this.samples)

    const blob = new Blob([view], {type: 'audio/wav'})

    return {
      id   : Date.now(),
      blob : blob,
      url  : URL.createObjectURL(blob)
    }
  }

  _floatTo16BitPCM (output, offset, input) {
    for (let i = 0; i < input.length; i++, offset += 2) {
      let s = Math.max(-1, Math.min(1, input[i]))
      output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true)
    }
  }

  _joinSamples () {
    let recordLength  = this.samples.length * this.bufferSize
    let joinedSamples = new Float64Array(recordLength)
    let offset        = 0

    for (let i = 0; i < this.samples.length; i++) {
      let sample = this.samples[i]
      joinedSamples.set(sample, offset)
      offset += sample.length
    }

    this.samples = joinedSamples
  }

  _writeString (view, offset, string) {
    for (let i = 0; i < string.length; i++) {
      view.setUint8(offset + i, string.charCodeAt(i))
    }
  }
}
